package com.yb.guigu.pick.upload.service.impl;

import com.yb.guigu.pick.minio.upload.bean.MinioProperties;
import com.yb.guigu.pick.upload.bean.MergeRequest;
import com.yb.guigu.pick.upload.bean.PutRequest;
import com.yb.guigu.pick.upload.client.UploadClient;
import com.yb.guigu.pick.upload.service.IUploadService;
import com.yb.guigu.pick.web.util.FileUtil;
import com.yb.guigu.pick.core.bean.GuiguResult;
import com.yb.guigu.pick.core.constant.MinioConstant;
import com.yb.guigu.pick.core.exception.GuiguBusinessException;
import com.yb.guigu.pick.core.util.Md5Util;
import com.yb.guigu.pick.core.util.PathUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Copyright (C), 2022-2023, 姚兵
 * Author: 32210
 * Date: 2023/6/2 15:18
 * FileName: UploadServiceImpl
 * Description:
 */

@Service
@Slf4j
public class UploadServiceImpl implements IUploadService {

    @Autowired
    MinioProperties minioProperties;

    @Autowired
    UploadClient uploadClient;
    @Override
    public GuiguResult upload(MultipartFile file) {

        PutRequest.PutRequestBuilder builder = PutRequest.builder();
        builder.filesize(file.getSize())
                .originFilename(file.getOriginalFilename());

        String fileMd5="";
        String suffix = FileUtil.getSuffix(Objects.requireNonNull(file.getOriginalFilename()));
        try {
            fileMd5 = Md5Util.getMd5(file.getInputStream());
            builder.uploadStream(file.getInputStream());
            builder.fileMD5(fileMd5);
        } catch (IOException e) {
            log.error("上传文件失败，失败原因:{0}",e);
            return GuiguResult.error(e.getMessage());
        }

        String currentTimeFolder = PathUtil.getCurrentTimeFolder();

        String chunkFolder = PathUtil.getChunkFolder(fileMd5);

        builder.bucket(minioProperties.getFileBucket().getName());
        String remoteFilepath=String.format(MinioConstant.IMAGE_FILENAME_PATTERN,currentTimeFolder,chunkFolder,fileMd5,suffix);
        builder.remoteFilepath(remoteFilepath );

        builder.contentType(FileUtil.getMimeType(file.getOriginalFilename()));

        PutRequest putRequest = builder.build();
        log.info("uploadRequest:{}",putRequest);
        if(!uploadClient.put(putRequest).isSuccess()){
            log.error("upload failed");
            throw new GuiguBusinessException("上传失败");
        }

        String url=String.format(MinioConstant.URL_PATTERN,minioProperties.getFileBucket().getName(),remoteFilepath);

        return GuiguResult.success().data(url);
    }

    @Override
    public GuiguResult uploadChunk(String fileMD5, Integer currentIndex,MultipartFile chunk) {
        PutRequest.PutRequestBuilder builder = PutRequest.builder();
        builder.filesize(chunk.getSize())
                .originFilename(chunk.getOriginalFilename());

        String chunkFolder = PathUtil.getChunkFolder(fileMD5);

        log.info("chunkFolder:{}",chunkFolder);

        String remoteFilepath = String.format(MinioConstant.CHUNK_PATH, chunkFolder,fileMD5, currentIndex.toString());

        builder.remoteFilepath(remoteFilepath);

        log.info("remoteFilepath:{}",remoteFilepath);
       try {
           builder.bucket(MinioConstant.BIG_FILE_BUCKET)
                   .uploadStream(chunk.getInputStream())
                   .contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
       }catch (Exception e){
           log.error("{}",e);
           throw new GuiguBusinessException("上传文件失败");
       }

        PutRequest putRequest = builder.build();

        log.info("uploadRequest:{}",putRequest);
        if(!uploadClient.put(putRequest).isSuccess()){
            log.error("upload failed");
            throw new GuiguBusinessException("上传失败");
        }

        HashMap<String, Object> map = new HashMap<>();
        map.put("fileMD5",fileMD5);
        map.put("currentIndex",currentIndex);
        return GuiguResult.success(map);
    }

    @Override
    public GuiguResult md5(MultipartFile file) {
        try {
            return GuiguResult.success(Md5Util.getMd5(file.getInputStream()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public GuiguResult mergeChunk(String fileMD5, Integer numOfChunk, String suffix) {

        String chunkFolder = PathUtil.getChunkFolder(fileMD5);

        List<String> chunkNameList = Stream.iterate(0, i -> i + 1)
                .limit(numOfChunk)
                .map(index ->
                        String.format(MinioConstant.CHUNK_PATH, chunkFolder, fileMD5, index))
                .collect(Collectors.toList());

        List<MergeRequest.ComposeFile> composeFiles = chunkNameList.stream().map(name ->
                MergeRequest.ComposeFile.builder().bucket(MinioConstant.BIG_FILE_BUCKET)
                        .remoteFilepath(name)
                        .build()
        ).collect(Collectors.toList());

        String finalPath = String.format(MinioConstant.MERGE_PATH, chunkFolder, fileMD5, suffix);
        MergeRequest mergeRequest = MergeRequest.builder().composeFiles(composeFiles)
                .bucket(MinioConstant.BIG_FILE_BUCKET)
                .remoteFilepath(finalPath).build();

        if (!uploadClient.merge(mergeRequest).isSuccess()) {
            log.error("上传失败");
            throw new GuiguBusinessException("上传失败");
        }

        HashMap<String, Object> map = new HashMap<>();
        map.put("fileMD5",fileMD5);
        map.put("fileUrl",String.format(MinioConstant.URL_PATTERN,MinioConstant.BIG_FILE_BUCKET,finalPath));
        return GuiguResult.success(map);
    }

    @Override
    public GuiguResult uploadChunkByChunkMd5(String chunkMd5, MultipartFile chunk) {
        PutRequest.PutRequestBuilder builder = PutRequest.builder();
        builder.filesize(chunk.getSize())
                .originFilename(chunk.getOriginalFilename());

        String chunkFolder = PathUtil.getChunkFolder(chunkMd5);

        log.info("chunkFolder:{}",chunkFolder);

        String remoteFilepath = String.format(MinioConstant.CHUNK_PATH2, chunkFolder,chunkMd5);

        builder.remoteFilepath(remoteFilepath);

        log.info("remoteFilepath:{}",remoteFilepath);
        try {
            builder.bucket(MinioConstant.BIG_FILE_BUCKET)
                    .uploadStream(chunk.getInputStream())
                    .contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        }catch (Exception e){
            log.error("{}",e);
            throw new GuiguBusinessException("上传文件失败");
        }

        PutRequest putRequest = builder.build();

        log.info("uploadRequest:{}",putRequest);
        if(!uploadClient.put(putRequest).isSuccess()){
            log.error("upload failed");
            throw new GuiguBusinessException("上传失败");
        }

        HashMap<String, Object> map = new HashMap<>();

        map.put("chunkMd5",chunkMd5);

        return GuiguResult.success(map);
    }


    @Override
    public GuiguResult mergeChunk(List<String> chunkMD5s, String fileMD5, String suffix) {
        log.info("mergeChunk By ChunkMD5 fileMD5:{} suffix:{}",fileMD5,suffix);
        String fileFolder=PathUtil.getChunkFolder(fileMD5);
        List<MergeRequest.ComposeFile> composeFiles = chunkMD5s.stream().map(md5 -> {
                    String chunkFolder = PathUtil.getChunkFolder(md5);
                    String remoteFilepath = String.format(MinioConstant.CHUNK_PATH2, chunkFolder, md5);
                    return MergeRequest.ComposeFile.builder().bucket(MinioConstant.BIG_FILE_BUCKET)
                            .remoteFilepath(remoteFilepath)
                            .build();
                })
                .collect(Collectors.toList());




        String finalPath = String.format(MinioConstant.MERGE_PATH, fileFolder, fileMD5, suffix);

        MergeRequest mergeRequest = MergeRequest.builder().composeFiles(composeFiles)
                .bucket(MinioConstant.BIG_FILE_BUCKET)
                .remoteFilepath(finalPath).build();

        if (!uploadClient.merge(mergeRequest).isSuccess()) {
            log.error("上传失败");
            throw new GuiguBusinessException("上传失败");
        }

        HashMap<String, Object> map = new HashMap<>();
        map.put("fileMD5",fileMD5);
        map.put("fileUrl",String.format(MinioConstant.URL_PATTERN,MinioConstant.BIG_FILE_BUCKET,finalPath));
        return GuiguResult.success(map);
    }
}
